require "capistrano/logger"
require 'set'
require 'stringio'

module Wondercap
module Logging
  
  class BaseLoggerWrapper
    def initialize(logger, cut_level)
      @logger = logger
      @cut_level = cut_level
    end
    
    def add(severity, str)
      return if severity > @cut_level
      
      if block_given?
        str = yield
      end
      @logger.log(severity, str)
    end
    
    def debug(str = nil, &block)
      add(Capistrano::Logger::DEBUG, str, &block)
    end
    
    def debug?
      @cut_level >= Capistrano::Logger::DEBUG
    end
    
    def info(str = nil, &block)
      add(Capistrano::Logger::INFO, str, &block)
    end
    
    def info?
      @cut_level >= Capistrano::Logger::INFO      
    end
    
    def error(str = nil)
      add(Capistrano::Logger::IMPORTANT, str, &block)
    end
    
    def error?
      @cut_level >= Capistrano::Logger::IMPORTANT      
    end
    
    def fatal(str = nil)
      add(Capistrano::Logger::IMPORTANT, str, &block)
    end
    
    def fatal?
      @cut_level >= Capistrano::Logger::IMPORTANT
    end
  end
  
  class StringLoggerDevice
    def initialize
      @io = StringIO.new
    end
    
    def puts(str)
      @io.puts str
    end
    
    def logger_output
      @io.string
    end
  end
  
  class StdErrLoggerDevice
    def puts(str)
      $stderr.puts str
      $stderr.flush
    end
  end
  
  class FileLoggerDevice
    def initialize(path)
      use_chmod = !File.exists?(path)
      @fio = open(path, "a")
      @fio.chmod 0600 if use_chmod
      at_exit { @fio.close}
    end
    
    def puts(str)
      @fio.puts("[#{Time.now}] #{str}")
      @fio.flush
    end
  end
  
  module LinePrefixedLoggerMixin
    attr :line_prefix, true
    
    def important(message, lpr = line_prefix)
      log(Capistrano::Logger::IMPORTANT, message, lpr)
    end

    def info(message, lpr = line_prefix)
      log(Capistrano::Logger::INFO, message, lpr)
    end

    def debug(message, lpr = line_prefix)
      log(Capistrano::Logger::DEBUG, message, lpr)
    end

    def trace(message, lpr = line_prefix)
      log(Capistrano::Logger::TRACE, message, lpr)
    end    
  end
  
  class CompositeLogger
    include LinePrefixedLoggerMixin

    attr :nested_loggers
    def initialize(*loggers)
      @nested_loggers = [].concat(loggers)
    end
    
    def log(level, message, line_prefix = nil)
      @nested_loggers.each { |l| l.log(level, message, line_prefix) }
    end
  end
  
  class MultiLevelLogger
    include LinePrefixedLoggerMixin    
    
    def initialize
      @composite_logger = CompositeLogger.new
      @leveled_loggers = []
      (0..Capistrano::Logger::MAX_LEVEL).each do |level|
        logger = Capistrano::Logger.new(:output => StringLoggerDevice.new)
        logger.level = level
        @leveled_loggers << logger
        @composite_logger.nested_loggers << logger
      end
    end
    
    def logger_for_level(level)
      @leveled_loggers[level]
    end
    
    def logger_output(lvl = nil)
      @leveled_loggers[lvl || level].device.logger_output
    end
    
    def log(level, message, line_prefix = nil)
      @composite_logger.log(level, message, line_prefix)
    end
        
  end

end

module CompositeMultiLevelLogging
  include Logging
  
  def logger
    unless @wcap_logger
      @ml_logger = MultiLevelLogger.new
      @wcap_logger = CompositeLogger.new(@ml_logger)
    end
    @wcap_logger
  end
  
  def logger_output(level)
    @ml_logger.logger_output(level)
  end
end

module AnotherLoggerEchoedLogging
  include Logging
  
  def logger
    unless @wcap_logger
      @ml_logger = MultiLevelLogger.new
      @wcap_logger = CompositeLogger.new(@ml_logger, outer_logger)
    end
    @wcap_logger
  end
  
  def logger_output(level)
    @ml_logger.logger_output(level)
  end
end

end